#pragma once

#include "../server/TcpServer.h"
#include "HttpContext.h"

#include <functional>
#include <string>

/*
httpserver:
in_buffer -> retirve a complete data
    -> parse to a http protocol -> request line/headr/body
        -> router -> make a response -> out_buffer
*/

class HttpServer {
private:
    using Handler = std::function<void(const Request&, Response*)>;
    using Handlers = std::unordered_map<std::string, Handler>;

    void HandleError(Response* response) {
        std::string err_path = wwwroot + "50x.html";
        bool ret = Utils::ReadFile(err_path, &response->body_);
        if (ret == false) {
            return;
        }
        response->SetValue("Content-Type", "text/html");
        return;
    }

    bool IsStaticRequest(const Request& request) {
        if (request.method_ != "GET" && request.method_ != "HEAD")
            return false;
        // TODO:路径安全性判断

        // 请求的资源必须存在,且是一个普通文件；当请求是目录，默认追加一个 index.html
        // /image/a.png  ->  ./wwwroot/image/a.png
        std::string req_path = wwwroot + request.url_;  // 为了避免直接修改请求的资源路径，因此定义一个临时对象
        if (request.url_.back() == '/') {
            req_path += "index.html";
        }
        // TODO
        return true;
    }

    void HandleStaticRequest(const Request& request, Response* response) {
        std::string req_path = wwwroot + request.url_;
        if (request.url_.back() == '/') {
            req_path += "index.html";
        }

        // 填充body
        bool ret = Utils::ReadFile(req_path, &response->body_);
        if (ret == false) {
            std::string err_path = wwwroot + "404.html";
            Utils::ReadFile(err_path, &response->body_);
            response->SetValue("Content-Type", "text/html");
            return;
        }
        std::string mime = Utils::GetMime(req_path);
        response->SetValue("Content-Type", mime);
        return;
    }

    void HandleApi(const Request& request, Response* response, Handlers& handlers) {
        // 在对应请求方法的路由表中，查找是否含有对应资源请求的处理函数，有则调用，否则404
        auto handler = handlers.find(request.url_);
        if (handler != handlers.end()) {
            const Handler funct = handler->second;
            funct(request, response);
            return;
        }
    }

    void SendResponse(const TcpConnectionPtr& conn, Response& response) {
        if (response.Body().empty() == false && response.HasKey("Content-Length") == false) {
            response.SetValue("Content-Length", std::to_string(response.Body().size()));
        }
        if (response.Body().empty() == false && response.HasKey("Content-Type") == false) {
            response.SetValue("Content-Type", "application/octet-stream");
        }
        response.SetValue("Connection", "keep-alive");

        std::string resp_str;
        resp_str = response.Version() + " " + response.Code() + " " + response.Describe() + "\r\n";
        for (auto& kv : response.kvs_) {
            std::string head = kv.first + ": " + kv.second + "\r\n";
            resp_str += head;
        }
        resp_str += "\r\n";
        resp_str += response.body_;

        conn->Send(resp_str.c_str());
    }

    void Router(const Request& request, Response* response) {
        // 静态资源请求/功能性请求
        // 1. 静态资源请求，则进行静态资源的处理
        // 2. 功能性请求，则需要通过几个请求路由表来确定是否有处理函数
        // 3. 否则返回405
        if (IsStaticRequest(request) == true) {
            LOG_INFO("static resources");
            return HandleStaticRequest(request, response);
        }

        if (request.method_ == "GET" || request.method_ == "HEAD") {
            return HandleApi(request, response, get_handler);
        } else if (request.method_ == "POST") {
            return HandleApi(request, response, post_handler);
        }
        response->status_code_ = "405";  // Method Not Allowed
        return;
    }

    void OnConnected(const TcpConnectionPtr& conn) {
        // 设置上下文
        LOG_INFO("have a new connection...");
        conn->SetHttpContext(HttpContext());
        // conn->Send("connect success!\n");
    }

    void OnMessage(const TcpConnectionPtr& conn, Buffer* in_buffer) {
        LOG_INFO("on message.");
        HttpContext* context = conn->GetHttpContext();
        context->ParseRequest(in_buffer);
        // 1. 如果缓冲区的数据解析出错，就直接返回出错响应
        // 2. 如果解析正常，且请求已经获取完毕，进行处理

        Request& request = context->GetRequest();
        Response response = context->GetResponse();

        // 出错处理
        if (context->HasError()) {
            HandleError(&response);
            SendResponse(conn, response);
            context->Reset();
            return;
        }

        // 不完整处理
        if (!context->IsComplete()) {
            return;
        }

        Router(request, &response);
        SendResponse(conn, response);
        context->Reset();
    }

    void SetThreadNum(int num = 3) { server_.SetThreadNum(num); }

public:
    HttpServer(EventLoop* loop, std::string name) : loop_(loop), server_(loop_, addr, name) {
        server_.SetConnectionCallback(std::bind(&HttpServer::OnConnected, this, std::placeholders::_1)),
            server_.SetMessageCallback(
                std::bind(&HttpServer::OnMessage, this, std::placeholders::_1, std::placeholders::_2));
    }

    void start() {
        SetThreadNum();
        server_.start();
    }

    void SetRootDir(const std::string path) {
        // 判断是否为一个文件夹
        wwwroot = path;
    }

    // 添加方法：
    void Get(const std::string api, const Handler& handler) {
        if (get_handler.find(api) != get_handler.end()) {
            get_handler[api] = handler;
            return;
        }
        get_handler.insert(std::make_pair(api, handler));
    }
    void Post(const std::string api, const Handler& handler) {
        if (post_handler.find(api) != post_handler.end()) {
            post_handler[api] = handler;
            return;
        }
        post_handler.insert(std::make_pair(api, handler));
    }

    ~HttpServer() {}

private:
    EventLoop* loop_;
    InetAddress addr;
    TcpServer server_;
    std::string wwwroot;

    Handlers get_handler;
    Handlers post_handler;
    Handlers put_handler;
    Handlers delete_handler;
};
